package com.cxyd.modules.wcs.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.cxyd.common.constant.Constants;
import com.cxyd.common.constant.OperateConstants;
import com.cxyd.common.constant.StatusConstants;
import com.cxyd.common.service.impl.CrudServiceImpl;
import com.cxyd.common.utils.ConvertUtils;
import com.cxyd.common.utils.ProcResult;
import com.cxyd.common.utils.StringUtils;
import com.cxyd.modules.api.dto.RcsResult;
import com.cxyd.modules.log.service.SysLogWorkService;
import com.cxyd.modules.wcs.dao.AgvTrkDao;
import com.cxyd.modules.wcs.dto.AgvTrkDTO;
import com.cxyd.modules.wcs.dto.AgvTrkJobDTO;
import com.cxyd.modules.wcs.dto.ApplyPassDTO;
import com.cxyd.modules.wcs.dto.PositionPathDTO;
import com.cxyd.modules.wcs.entity.AgvTrkEntity;
import com.cxyd.modules.wcs.entity.TrkEntity;
import com.cxyd.modules.wcs.service.AgvTrkService;
import com.cxyd.modules.wcs.service.PlcItemService;
import com.cxyd.modules.wcs.service.TrkService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class AgvTrkServiceImpl extends CrudServiceImpl<AgvTrkDao, AgvTrkEntity, AgvTrkDTO> implements AgvTrkService {
    @Resource
    private TrkService trkService;
    @Resource
    private PlcItemService itemService;
    @Resource
    private SysLogWorkService logWorkService;

    @Override
    public QueryWrapper<AgvTrkEntity> getWrapper(Map<String, Object> params) {
        return null;
    }

    @Override
    public AgvTrkJobDTO getOneRecord(String dvcNo) {
//        AB两个区域下发出库任务时都需要考虑作业站台的缓存容量(线体上料箱+已下发的出库任务<7)
        String sqlStr;
        if ("CV01".equals(dvcNo)) {
            sqlStr = "SELECT 1 from agv_trk where wb_code='CV01' and task_typ='T100' and status in(1,9)";
        } else {
            sqlStr = "SELECT 1 from agv_trk where wb_code='CV02' and task_typ='T102' and status in(1,9)";
        }
//        优先级顺序：入库>出库>优先级>生成时间
        QueryWrapper<AgvTrkEntity> qw = new QueryWrapper<>();
        qw.lambda().eq(AgvTrkEntity::getStatus, 0)
                .eq(AgvTrkEntity::getWbCode, dvcNo)
                .notExists(true, sqlStr)
                .orderBy(true, true, AgvTrkEntity::getTaskTyp)
                .orderBy(true, false, AgvTrkEntity::getPriority)
                .orderBy(true, true, AgvTrkEntity::getCreateDate)
                .last("limit 1");
        AgvTrkEntity agvTrkEntity = baseDao.selectOne(qw);
        if (agvTrkEntity == null) {
            return null;
        }
//        判断如果是出库任务则做个数量校验（task_type=T101&T103）
        if ("T101".equals(agvTrkEntity.getTaskTyp()) || "T103".equals(agvTrkEntity.getTaskTyp())) {
            int volumeQty = baseDao.getVolumeByDvcNo(dvcNo);
            int trkQty = baseDao.getAgvTrkSumByState(agvTrkEntity.getTaskTyp());
            if (volumeQty + trkQty > StatusConstants.MAX_VOLUME) {
                return null;
            }
        }

//        根据trk_id获取wcs_trk
        List<PositionPathDTO> pathList = trkService.selectPathById(agvTrkEntity.getTrkId());
        AgvTrkJobDTO jobDto = ConvertUtils.sourceToTarget(agvTrkEntity, AgvTrkJobDTO.class);
//        写入reqTime
        String reqTime = DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss");
        jobDto.setWbCode(null);
        jobDto.setPositionCodePath(pathList);
        jobDto.setReqTime(reqTime);
        return jobDto;
    }

    @Override
    public ApplyPassDTO getPassRecord() {
        QueryWrapper<AgvTrkEntity> qw = new QueryWrapper<>();
        qw.lambda().eq(AgvTrkEntity::getStatus, 30)
                .orderBy(true, true, AgvTrkEntity::getUpdateDate)
                .last("limit 1");
        AgvTrkEntity agvTrkEntity = baseDao.selectOne(qw);
        if (agvTrkEntity == null) {
            return null;
        }
//        GET为1,PUT为2,当前位置从[position_code_path]获取
        String applyType = "1".equals(agvTrkEntity.getAgvTyp()) ? "GET" : "PUT";
        boolean flag = itemService.signalJudge(agvTrkEntity.getCtnrCode(), applyType, agvTrkEntity.getPositionCodePath());
        if (!flag) {
//            更新agv_trk相关信息
            agvTrkEntity.setData("线体暂不可取放!请等待...");
            agvTrkEntity.setUpdateDate(new Date());
            baseDao.updateById(agvTrkEntity);
            return null;
        }
//        判断当前站台是否可取或可放(若不允许则直接返回null,等待下次扫描请求)
        ApplyPassDTO dto = new ApplyPassDTO();
//        reqCode不能重复使用,此处只能重新生成
        dto.setReqCode(String.valueOf(System.currentTimeMillis()));
        dto.setTaskCode(agvTrkEntity.getTaskCode());
        dto.setClientCode(agvTrkEntity.getClientCode());
        dto.setReqTime(DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
        dto.setTokenCode(agvTrkEntity.getTokenCode());
        dto.setType(agvTrkEntity.getAgvTyp());
        return dto;
    }

    @Override
    public AgvTrkEntity getAgvTrkByContNo(String contNo) {
        if (StringUtils.isEmpty(contNo)) {
            return null;
        }
        QueryWrapper<AgvTrkEntity> qw = new QueryWrapper<>();
        qw.eq("ctnr_code", contNo);
        return baseDao.selectOne(qw);
    }

    @Override
    public AgvTrkEntity getAgvTryByTaskCode(String taskCode) {
        if (StringUtils.isEmpty(taskCode)) {
            return null;
        }
        return baseDao.getAgvTryByTaskCode(taskCode);
    }

    @Override
    public ProcResult agvTrkOperate(Map<String, Object> params) {
        String id = (String) params.get("id");
        String operateType = (String) params.get("operateType");
        if (StrUtil.isBlankIfStr(id)) {
            return new ProcResult().result("101", "id不能为空!");
        }
        if (StrUtil.isBlankIfStr(operateType)) {
            return new ProcResult().result("102", "operateType不能为空!");
        }
        String workName = "CTU任务操作";
        String workProc = "AgvTrkService.agvTrkOperate";
        String pCode = "0";
        String pMsg = "[" + operateType + "]操作成功!id[" + id + "]";
//        根据id查出对应的agv_trk任务
        QueryWrapper<AgvTrkEntity> agvQw = new QueryWrapper<>();
        agvQw.eq("id", Long.valueOf(id));
        AgvTrkEntity agvTrk = baseDao.selectOne(agvQw);
        if (agvTrk == null) {
            pCode = "agvTrkOperate_error_001";
            pMsg = "id[" + id + "]的任务不存在!请核查";
            logWorkService.workLogging(workName, "", pCode, pMsg, "", workProc, "WCS系统", 0);
            return new ProcResult().result(pCode, pMsg);
        }
        int status = 9;
//        根据不同的操作实现不同的逻辑（CANCEL/REPEAT/DELETE/DONE）
        if (OperateConstants.TASK_MANUAL_CANCEL_OPERATE.equals(operateType)) {
//            1.任务取消（更新agv任务状态与描述）
            pMsg = "料箱[" + agvTrk.getCtnrCode() + "]搬送任务取消成功!";
            if (agvTrk.getStatus().equals(9)) {
                return new ProcResult().result(pCode, pMsg);
            }
        } else if (OperateConstants.TASK_FORCE_REPEAT_OPERATE.equals(operateType)) {
            //        非9[取消/异常]任务无法进行任务重发
            if (!agvTrk.getStatus().equals(9)) {
                pCode = "agvTrkOperate_error_003";
                pMsg = "CTU任务[" + id + "]的当前状态[" + agvTrk.getStatus() + "]不允许重发!请先取消任务";
            } else {
                //            2.任务重发（需重置req_code与task_code）
                status = 0;
                pMsg = "料箱[" + agvTrk.getCtnrCode() + "]的搬送任务重发成功!";
                String reqCode = "REPEAT" + System.currentTimeMillis();
                String taskCode = "TASK" + System.currentTimeMillis();
                agvTrk.setReqCode(reqCode);
                agvTrk.setTaskCode(taskCode);
            }
        } else if (OperateConstants.TASK_MANUAL_DELETE_OPERATE.equals(operateType)) {
//            3.任务删除
            status = 9;
            pMsg = "id[" + id + "]的任务删除成功!";
        } else if (OperateConstants.TASK_FORCE_DONE_OPERATE.equals(operateType)) {
//            4.任务完成
            status = 4;
            pMsg = "id[" + id + "]的任务强制完成!";
            //更新wcs_trk与wcs_receive,同时写入post_wms
        } else {
//            对于不能处理的操作方式
            pCode = "agvTrkOperate_error_002";
            pMsg = "操作方式[" + operateType + "]暂无法处理!请咨询开发人员";
            return new ProcResult().result(pCode, pMsg);
        }
        agvTrk.setStatus(status);
        agvTrk.setData(pMsg);
        agvTrk.setUpdateDate(new Date());
        baseDao.updateById(agvTrk);
        return new ProcResult().result(pCode, pMsg);
    }

    @Override
    public void agvTrkHandle(AgvTrkJobDTO dto, RcsResult response) {
//        1.判断任务是否下发成功
        String msg = response.getMessage();
        String reqCode = response.getReqCode();
//        2.成功下发更新任务状态为执行中,失败则记录失败原因
        if (StringUtils.isEmpty(reqCode)) {
            return;
        }
//        3.定义状态
        String pCode = "0";
        String pMsg = "[step-2]料箱[" + dto.getCtnrCode() + "]搬送任务下发成功!taskCode[" + dto.getTaskCode() + "]";
        int status;
        if (Constants.SUCCESS.equals(response.getCode())) {
            status = 1;
//            成功下发更新[wcs_trk]任务状态
        } else {
            status = 9;
            pCode = "agvTrkHandle_error_001";
            pMsg = "料箱[" + dto.getCtnrCode() + "]搬送任务下发失败!rcs反馈异常:" + msg;
        }
//        开始时间-start
        Instant start = Instant.now();
//        4.条件更新语句
        UpdateWrapper<AgvTrkEntity> uw = new UpdateWrapper<>();
        uw.lambda().set(AgvTrkEntity::getStatus, status)
                .set(AgvTrkEntity::getData, msg)
                .set(AgvTrkEntity::getReqTime, DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"))
                .set(AgvTrkEntity::getUpdater, 1002L)
                .set(AgvTrkEntity::getUpdateDate, new Date())
                .eq(AgvTrkEntity::getReqCode, reqCode);
        baseDao.update(null, uw);
//        结束时间-end
        Instant end = Instant.now();
//        5.日志记录
        String workName = "下发RCS指令";
        String workProc = "AgvTrkService.agvTrkHandle";
//        计算耗时
        Duration times = Duration.between(start, end);
        logWorkService.workLogging(workName, dto.getCtnrCode(), pCode, pMsg, "", workProc, "WCS系统", times.toMillis());
    }

    @Override
    public void agvPassHandle(ApplyPassDTO dto, RcsResult response) {
//        1.判断任务是否下发成功
//        2.成功下发更新任务状态为执行中,失败则记录失败原因
        if (StringUtils.isEmpty(response.getReqCode())) {
            return;
        }
        String msg = response.getMessage();
//        通过ReqCode获取取放申请的任务
        QueryWrapper<AgvTrkEntity> qw = new QueryWrapper<>();
        qw.lambda().eq(AgvTrkEntity::getTaskCode, dto.getTaskCode());
        AgvTrkEntity agvTrk = baseDao.selectOne(qw);
        String pCode = "0";
        String pMsg = "[step-3]料箱[" + agvTrk.getCtnrCode() + "]取放凭证下发RCS!申请类型[" + agvTrk.getAgvTyp() + "]";
        int status;
        if (Constants.SUCCESS.equals(response.getCode())) {
            status = 3;
//            成功下发更新[wcs_trk]任务状态
        } else {
            status = 9;
            pCode = "agvPassHandle_error_001";
            pMsg = "料箱[" + agvTrk.getCtnrCode() + "]取放凭证下发失败!申请类型[" + agvTrk.getAgvTyp() + "]rcs反馈异常:" + msg;
        }
//        更新agv_trk状态
        agvTrk.setStatus(status);
        agvTrk.setData(pMsg);
        agvTrk.setUpdateDate(new Date());
        baseDao.updateById(agvTrk);
        String workName = "下发CTU取放凭证";
        String workProc = "AgvTrkService.agvPassHandle";
        logWorkService.workLogging(workName, agvTrk.getCtnrCode(), pCode, pMsg, "", workProc, "WCS系统", 0);
    }

    @Override
    public void updateAgvTrkByContNo(String contNo) {
        QueryWrapper<AgvTrkEntity> qw = new QueryWrapper<>();
        qw.lambda().eq(AgvTrkEntity::getCtnrCode, contNo)
                .eq(AgvTrkEntity::getStatus, 4);
        AgvTrkEntity agvTrkEntity = baseDao.selectOne(qw);
//        1.转移历史表
        baseDao.backupAgvTrk(contNo);
        baseDao.deleteById(agvTrkEntity);
    }
}
